#include <wx/wx.h>
#include "panel_delay.h"
#include "MyCommon.h"
#include "DataSave.h"

#define SLI_MAX(max,min,step) (int)((max - min) / step)


wxDEFINE_EVENT(myEVT_DELAY, MyEventDelay);
wxDEFINE_EVENT(myEVT_DELAY_TONE, MyEventDelay);
wxDEFINE_EVENT(myEVT_DELAY_WET, MyEventDelay);
wxDEFINE_EVENT(myEVT_DELAY_FEEDBACK, MyEventDelay);
wxDEFINE_EVENT(myEVT_DELAY_TIME, MyEventDelay);
wxDEFINE_EVENT(myEVT_DELAY_ENABLE, MyEventDelay);


// wxBEGIN_EVENT_TABLE(PanelDelay, wxPanel)
//     EVT_SLIDER(PanelDelay::ID_SLI_TONE, PanelDelay::OnSliderChange)
//     EVT_SLIDER(PanelDelay::ID_SLI_WET, PanelDelay::OnSliderChange)
//     EVT_SLIDER(PanelDelay::ID_SLI_FEEDBACK, PanelDelay::OnSliderChange)
//     EVT_SLIDER(PanelDelay::ID_SLI_TIME, PanelDelay::OnSliderChange)
//     EVT_TEXT_ENTER(PanelDelay::ID_TXT_TONE,PanelDelay::OnTextEnter)
//     EVT_TEXT_ENTER(PanelDelay::ID_TXT_WET,PanelDelay::OnTextEnter)
//     EVT_TEXT_ENTER(PanelDelay::ID_TXT_FEEDBACK,PanelDelay::OnTextEnter)
//     EVT_TEXT_ENTER(PanelDelay::ID_TXT_TIME,PanelDelay::OnTextEnter)
//     EVT_CHECKBOX(PanelDelay::ID_CHK_SWITCH,PanelDelay::OnChkChange)
// wxEND_EVENT_TABLE()


PanelDelay::PanelDelay(const wxString &title, wxXmlNode *node, wxWindowID id, wxWindow *parent)
    : wxPanel(parent,id,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL)
{
    SetSize(200, 300);

    root_node = node;
    select_index = 0;

    wxWindowID cid = id + 1;
    ID_TXT_TONE = cid++;
    ID_TXT_WET = cid++;
    ID_TXT_FEEDBACK = cid++;
    ID_TXT_TIME = cid++;
    ID_SLI_TONE = cid++;
    ID_SLI_WET = cid++;
    ID_SLI_FEEDBACK = cid++;
    ID_SLI_TIME = cid++;
    ID_CHK_SWITCH = cid++;
    ID_CHO_PRESET = cid ++;


    wxCheckBox      *chk_enable   = new wxCheckBox(this  , ID_CHK_SWITCH  , "Enable");
    wxTextCtrl      *txt_tone      = new wxTextCtrl(this  , ID_TXT_TONE    , mDoubleToString(tone_min)     , wxDefaultPosition, wxDefaultSize       , wxTE_PROCESS_ENTER);
    wxTextCtrl      *txt_wet      = new wxTextCtrl(this  , ID_TXT_WET     , mDoubleToString(wet_min)     , wxDefaultPosition, wxDefaultSize       , wxTE_PROCESS_ENTER);
    wxTextCtrl      *txt_feedback = new wxTextCtrl(this  , ID_TXT_FEEDBACK, mDoubleToString(feedback_min), wxDefaultPosition, wxDefaultSize       , wxTE_PROCESS_ENTER);
    wxTextCtrl      *txt_time     = new wxTextCtrl(this  , ID_TXT_TIME    , mDoubleToString(time_min)    , wxDefaultPosition, wxDefaultSize       , wxTE_PROCESS_ENTER);
    wxStaticText    *lab_tone      = new wxStaticText(this, wxID_ANY       , "Tone:");
    wxStaticText    *lab_wet      = new wxStaticText(this, wxID_ANY       , "Wet:");
    wxStaticText    *lab_feedback = new wxStaticText(this, wxID_ANY       , "Feedback:");
    wxStaticText    *lab_time     = new wxStaticText(this, wxID_ANY       , "Time:");
    wxStaticText    *lab_preset   = new wxStaticText(this, wxID_ANY       , "Preset:");
    wxSlider        *sli_tone      = new wxSlider(this    , ID_SLI_TONE    , 0                            , 0                , SLI_MAX(tone_max     , tone_min             , tone_step));
    wxSlider        *sli_wet      = new wxSlider(this    , ID_SLI_WET     , 0                            , 0                , SLI_MAX(wet_max     , wet_min             , wet_step));
    wxSlider        *sli_feedback = new wxSlider(this    , ID_SLI_FEEDBACK, 0                            , 0                , SLI_MAX(feedback_max, feedback_min        , feedback_step));
    wxSlider        *sli_time     = new wxSlider(this    , ID_SLI_TIME    , 0                            , 0                , SLI_MAX(time_max    , time_min            , time_step));
    wxFlexGridSizer *layout_main  = new wxFlexGridSizer(6, 3              , wxSize(3                     , 3));
    wxChoice        *cho_preset      = new wxChoice(this,     ID_CHO_PRESET);

    sli_tone->SetMinSize(wxSize(100, -1));
    sli_tone->SetTick(1);
    sli_wet->SetMinSize(wxSize(100, -1));
    sli_feedback->SetMinSize(wxSize(100, -1));
    sli_time->SetMinSize(wxSize(100, -1));
    txt_tone->SetMinSize(wxSize(50, -1));
    txt_tone->SetMaxSize(wxSize(100, -1));
    txt_wet->SetMinSize(wxSize(50, -1));
    txt_wet->SetMaxSize(wxSize(100, -1));
    txt_feedback->SetMinSize(wxSize(50, -1));
    txt_feedback->SetMaxSize(wxSize(100, -1));
    txt_time->SetMinSize(wxSize(50, -1));
    txt_time->SetMaxSize(wxSize(100, -1));

    sli_feedback->Bind(wxEVT_SLIDER, &PanelDelay::OnSliderChange, this, ID_SLI_FEEDBACK);
    sli_tone->Bind(wxEVT_SLIDER, &PanelDelay::OnSliderChange, this, ID_SLI_TONE);
    sli_wet->Bind(wxEVT_SLIDER, &PanelDelay::OnSliderChange, this, ID_SLI_WET);
    sli_time->Bind(wxEVT_SLIDER, &PanelDelay::OnSliderChange, this, ID_SLI_TIME);
    txt_tone->Bind(wxEVT_TEXT_ENTER, &PanelDelay::OnTextEnter, this, ID_TXT_TONE);
    txt_wet->Bind(wxEVT_TEXT_ENTER, &PanelDelay::OnTextEnter, this, ID_TXT_WET);
    txt_feedback->Bind(wxEVT_TEXT_ENTER, &PanelDelay::OnTextEnter, this, ID_TXT_FEEDBACK);
    txt_time->Bind(wxEVT_TEXT_ENTER, &PanelDelay::OnTextEnter, this, ID_TXT_TIME);
    chk_enable->Bind(wxEVT_CHECKBOX, &PanelDelay::OnChkChange, this, ID_CHK_SWITCH);
    cho_preset->Bind(wxEVT_CHOICE,        &PanelDelay::OnCombChange,   this, ID_CHO_PRESET);

    // cho_preset->Append("delay-1");
    // cho_preset->Append("delay-2");
    // cho_preset->Append("delay-3");
    // cho_preset->Append("delay-4");
    // cho_preset->Append("delay-5");
    // cho_preset->Append("delay-6");
    // cho_preset->Select(select_index);


    layout_main->Add(chk_enable  ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->AddStretchSpacer();
    layout_main->AddStretchSpacer();
    layout_main->Add(lab_tone     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(txt_tone     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(sli_tone     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(lab_wet     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(txt_wet     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(sli_wet     ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(lab_feedback,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(txt_feedback,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(sli_feedback,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(lab_time    ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(txt_time    ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(sli_time    ,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(lab_preset,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->Add(cho_preset,0,wxALIGN_CENTER_VERTICAL | wxEXPAND);
    layout_main->AddStretchSpacer();
    layout_main->AddGrowableCol(0,0);
    layout_main->AddGrowableCol(1,0);
    layout_main->AddGrowableCol(2,1);

    SetSizer(layout_main);

    paneInfo.Caption(title);
    paneInfo.Dock();
    paneInfo.Dockable(true);
    paneInfo.CloseButton(true);
    paneInfo.MaximizeButton(true);
    paneInfo.MinimizeButton(true);
    paneInfo.DestroyOnClose(false);
    // paneInfo.Direction(wxLeft);

    param_event.SetEventObject(this);

    param.tone = tone_min;
    param.wet = wet_min;
    param.feedback = feedback_min;
    param.time_ms = time_min;
    param.enable = false;

    // wxString str;
    // wxString select_name = attr_name_preset_default;
    // if(DataSave::NodeAttributesGetValue(root_node,attr_name_select,str))
    // {
    //     select_name = str;
    // }
    // else
    // {
    //     DataSave::NodeAttributesSetValue(root_node,attr_name_select,attr_name_preset_default);
    // }
    preset_node = nullptr;
    if(cho_preset->GetCount() > 0)
    {
        preset_node = SelectPreset(cho_preset->GetString(select_index));
    }
}


wxAuiPaneInfo &PanelDelay::GetPaneInfo()
{
    return paneInfo;
}


void PanelDelay::SetShow(bool show)
{
    Show(show);
}


void PanelDelay::OnSliderChange(wxCommandEvent& event)
{
    int id = event.GetId();
    // switch(id)
    {
        if(id == ID_SLI_TONE)
        {
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TONE),wxSlider);
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TONE),wxTextCtrl);
            double sv = sli->GetValue();
            double v = tone_min + sv / (1/tone_step);
            txt->SetValue(mDoubleToString(v));
            param.tone = v;
            TrigerEvent(EV_TONE);
        }
        else if(id ==  ID_SLI_WET)
        {
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_WET),wxSlider);
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_WET),wxTextCtrl);
            double sv = sli->GetValue();
            double v = wet_min + sv / (1/wet_step);
            txt->SetValue(mDoubleToString(v));
            param.wet = v;
            TrigerEvent(EV_WET);
        }
        else if(id ==  ID_SLI_TIME)
        {
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TIME),wxSlider);
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TIME),wxTextCtrl);
            double sv = sli->GetValue();
            double v = time_min + sv / (1/time_step);
            txt->SetValue(mDoubleToString(v));
            param.time_ms = v;
            TrigerEvent(EV_TIME);
        }
        else if(id ==  ID_SLI_FEEDBACK)
        {
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_FEEDBACK),wxSlider);
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_FEEDBACK),wxTextCtrl);
            double sv = sli->GetValue();
            double v = feedback_min + sv / (1/feedback_step);
            txt->SetValue(mDoubleToString(v));
            param.feedback = v;
            TrigerEvent(EV_FEEDBACK);
        }
    }
}

void PanelDelay::OnTextEnter(wxCommandEvent& event)
{
    int id = event.GetId();
    // switch(id)
    {
        if(id == ID_TXT_TONE)
        {
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TONE),wxTextCtrl);
            double v ;
            if(txt->GetValue().ToDouble(&v) == true)
            {
                if((v >= tone_min) && (v <= tone_max))
                {
                    param.tone = v;
                }
            }
            double sv = (param.tone - tone_min) * (1/tone_step);
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TONE),wxSlider);
            sli->SetValue(sv);
            TrigerEvent(EV_TONE);
        }
        else if(id ==  ID_TXT_WET)
        {
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_WET),wxTextCtrl);
            double v ;
            if(txt->GetValue().ToDouble(&v) == true)
            {
                if((v >= wet_min) && (v <= wet_max))
                {
                    param.wet = v;
                }
            }
            double sv = (param.wet - wet_min) * (1/wet_step);
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_WET),wxSlider);
            sli->SetValue(sv);
            TrigerEvent(EV_WET);
        }
        else if(id ==  ID_TXT_TIME)
        {
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TIME),wxTextCtrl);
            double v ;
            if(txt->GetValue().ToDouble(&v) == true)
            {
                if((v >= time_min) && (v <= time_max))
                {
                    param.time_ms = v;
                }
            }
            double sv = (param.time_ms - time_min) * (1/time_step);
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TIME),wxSlider);
            sli->SetValue(sv);
            TrigerEvent(EV_TIME);
        }
        else if(id ==  ID_TXT_FEEDBACK)
        {
            wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_FEEDBACK),wxTextCtrl);
            double v ;
            if(txt->GetValue().ToDouble(&v) == true)
            {
                if((v >= feedback_min) && (v <= feedback_max))
                {
                    param.feedback = v;
                }
            }
            double sv = (param.feedback - feedback_min) * (1/feedback_step);
            wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_FEEDBACK),wxSlider);
            sli->SetValue(sv);
            TrigerEvent(EV_FEEDBACK);
        }
    }
}

void PanelDelay::OnChkChange(wxCommandEvent &event)
{
    int id = event.GetId();
    // switch(id)
    {
        if(id == ID_CHK_SWITCH)
        {
            wxCheckBox *chk = wxDynamicCast(FindWindowById(ID_CHK_SWITCH),wxCheckBox);
            param.enable = chk->IsChecked();
            TrigerEvent(EV_ENABLE);
        }
    }
}

void PanelDelay::OnCombChange(wxCommandEvent& event)
{
    wxChoice *cmb = wxDynamicCast(FindWindowById(ID_CHO_PRESET),wxChoice);
    int i = cmb->GetSelection();
    if((unsigned int)i < cmb->GetCount())
    {
        select_index = i;
        preset_node = SelectPreset(cmb->GetString(i));
        param_event.SetPresetIndex(select_index);
        param_event.SetEventType(myEVT_DELAY);
        ProcessWindowEvent(param_event);
    }
}


double PanelDelay::SetParamTone(double v)
{
    if(v < tone_min)
    {
        v = tone_min;
    }
    else if(v > tone_max)
    {
        v = tone_max;
    }
    double sv = (v - tone_min) * (1/tone_step);
    wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TONE),wxSlider);
    sli->SetValue(sv);
    wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TONE),wxTextCtrl);
    txt->SetValue(mDoubleToString(v));
    param.tone = v;
    param_event.SetTone(v);
    return v;
}

double PanelDelay::SetParamWet(double v)
{
    if(v < wet_min)
    {
        v = wet_min;
    }
    else if(v > wet_max)
    {
        v = wet_max;
    }
    double sv = (v - wet_min) * (1/wet_step);
    wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_WET),wxSlider);
    sli->SetValue(sv);
    wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_WET),wxTextCtrl);
    txt->SetValue(mDoubleToString(v));
    param.wet = v;
    param_event.SetWet(v);
    return v;
}

double PanelDelay::SetParamTime(double v)
{
    if(v < time_min)
    {
        v = time_min;
    }
    else if(v > time_max)
    {
        v = time_max;
    }
    double sv = (v - time_min) * (1/time_step);
    wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_TIME),wxSlider);
    sli->SetValue(sv);
    wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_TIME),wxTextCtrl);
    txt->SetValue(mDoubleToString(v));
    param.time_ms = v;
    param_event.SetTime(v);
    return v;
}

double PanelDelay::SetParamFeedback(double v)
{
    if(v < feedback_min)
    {
        v = feedback_min;
    }
    else if(v > feedback_max)
    {
        v = feedback_max;
    }
    double sv = (v - feedback_min) * (1/feedback_step);
    wxSlider *sli = wxDynamicCast(FindWindowById(ID_SLI_FEEDBACK),wxSlider);
    sli->SetValue(sv);
    wxTextCtrl *txt = wxDynamicCast(FindWindowById(ID_TXT_FEEDBACK),wxTextCtrl);
    txt->SetValue(mDoubleToString(v));
    param.feedback = v;
    param_event.SetFeedback(v);
    return v;
}

bool PanelDelay::SetParamEnable(bool v)
{
    wxCheckBox *chk = wxDynamicCast(FindWindowById(ID_CHK_SWITCH),wxCheckBox);
    chk->SetValue(v);
    param.enable = v;
    param_event.SetEnable(v);
    return v;
}

double PanelDelay::GetParamTone() { return param.tone; }
double PanelDelay::GetParamWet() { return param.wet; }
double PanelDelay::GetParamFeedback() { return param.feedback; }
double PanelDelay::GetParamTime() { return param.time_ms; }
bool PanelDelay::GetParamEnable() { return param.enable; }


void PanelDelay::SetPresetIndex(const unsigned int index)
{
    wxChoice *cmb = wxDynamicCast(FindWindowById(ID_CHO_PRESET),wxChoice);
    if(index < cmb->GetCount())
    {
        select_index = index;
        param_event.SetPresetIndex(select_index);
        cmb->SetSelection(index);
        preset_node = SelectPreset(cmb->GetString(index));
    }
}

int PanelDelay::GetPresetIndex()
{
    return select_index;
}

void PanelDelay::TrigerEvent(int ev)
{
    switch(ev)
    {
        case EV_TONE:
        {
            param_event.SetEventType(myEVT_DELAY_TONE);
            param_event.SetTone(param.tone);
            ProcessWindowEvent(param_event);
            DataSave::NodeAttributesSetValue(preset_node, attr_name_param_tone, mDoubleToString(param.tone).c_str());
        }
        break;
        case EV_WET:
        {
            param_event.SetEventType(myEVT_DELAY_WET);
            param_event.SetWet(param.wet);
            ProcessWindowEvent(param_event);
            DataSave::NodeAttributesSetValue(preset_node, attr_name_param_wet, mDoubleToString(param.wet).c_str());
        }
        break;
        case EV_TIME:
        {
            param_event.SetEventType(myEVT_DELAY_TIME);
            param_event.SetTime(param.time_ms);
            ProcessWindowEvent(param_event);
            DataSave::NodeAttributesSetValue(preset_node, attr_name_param_time, mDoubleToString(param.time_ms).c_str());
        }
        break;
        case EV_FEEDBACK:
        {
            param_event.SetEventType(myEVT_DELAY_FEEDBACK);
            param_event.SetFeedback(param.feedback);
            ProcessWindowEvent(param_event);
            DataSave::NodeAttributesSetValue(preset_node, attr_name_param_feedback, mDoubleToString(param.feedback).c_str());
        }
        break;
        case EV_ENABLE:
        {
            param_event.SetEventType(myEVT_DELAY_ENABLE);
            param_event.SetEnable(param.enable);
            ProcessWindowEvent(param_event);
            DataSave::NodeAttributesSetValue(preset_node, attr_name_param_enable, param.enable ? "1" : "0");
        }
        break;
    }
}


wxXmlNode *PanelDelay::SelectPreset(wxString name)
{
    double tone = param.tone;
    double wet = param.wet;
    double feedback = param.feedback;
    double time_ms = param.time_ms;
    bool enable = param.enable;

    wxString str;
    wxXmlNode *child;
    child = root_node->GetChildren();
    while(child)
    {
        if(strcmp(child->GetName().c_str(), name.c_str()) == 0)
        {
            break;
        }
        child = child->GetNext();
    }

    if(DataSave::NodeAttributesGetValue(child,attr_name_param_enable,str))
    {
        int v;
        if(str.ToInt(&v))
        {
            enable = v ? true : false;
        }
    }

    if(DataSave::NodeAttributesGetValue(child,attr_name_param_tone,str))
    {
        double v;
        if(str.ToDouble(&v))
        {
            tone = v;
        }
    }

    if(DataSave::NodeAttributesGetValue(child,attr_name_param_wet,str))
    {
        double v;
        if(str.ToDouble(&v))
        {
            wet = v;
        }
    }

    if(DataSave::NodeAttributesGetValue(child,attr_name_param_feedback,str))
    {
        double v;
        if(str.ToDouble(&v))
        {
            feedback = v;
        }
    }

    if(DataSave::NodeAttributesGetValue(child,attr_name_param_time,str))
    {
        double v;
        if(str.ToDouble(&v))
        {
            time_ms = v;
        }
    }

    SetParamEnable(enable);
    SetParamTone(tone);
    SetParamWet(wet);
    SetParamFeedback(feedback);
    SetParamTime(time_ms);

    // if(child == NULL)
    // {
    //     child = new wxXmlNode(wxXML_ELEMENT_NODE,name);
    //     WriteToXML(child);
    //     root_node->AddChild(child);
    // }
    return child;
}

void PanelDelay::PresetListClear(void)
{
    wxChoice *cmb = wxDynamicCast(FindWindowById(ID_CHO_PRESET),wxChoice);
    if(cmb)
    {
        cmb->Clear();
        preset_node = nullptr;
    }
}


void PanelDelay::AddPresetList(const wxString &preset_name)
{
    wxChoice *cmb = wxDynamicCast(FindWindowById(ID_CHO_PRESET),wxChoice);
    if(cmb)
    {
        cmb->Append(preset_name);
    }
}


wxXmlNode *PanelDelay::SaveCurrentParamsToNewPreset(wxXmlNode *root, const wxString &new_preset_name,int &is_same)
{
    wxXmlNode *child = nullptr;
    is_same = 0;
    if((root != nullptr) && (new_preset_name.empty() == false))
    {
        //查找有没有名字相同的预设
        child = root->GetChildren();
        while(child)
        {
            if(strcmp(child->GetName().c_str(), new_preset_name.c_str()) == 0)
            {
                is_same = 1;
                return nullptr;
            }
            child = child->GetNext();
        }
        child = new wxXmlNode(wxXML_ELEMENT_NODE,new_preset_name);
        WriteToXML(child);
        root->AddChild(child);
        wxChoice *cmb = wxDynamicCast(FindWindowById(ID_CHO_PRESET),wxChoice);
        if(cmb)
        {
            cmb->Append(new_preset_name);
        }
    }
    return child;
}

bool PanelDelay::WriteToXML(wxXmlNode *node)
{
    if(node)
    {
        DataSave::NodeAttributesSetValue(node, attr_name_param_enable, param.enable ? "1" : "0");
        DataSave::NodeAttributesSetValue(node, attr_name_param_tone, mDoubleToString(param.tone).c_str(), true);
        DataSave::NodeAttributesSetValue(node, attr_name_param_wet, mDoubleToString(param.wet).c_str(), true);
        DataSave::NodeAttributesSetValue(node, attr_name_param_feedback, mDoubleToString(param.feedback).c_str(), true);
        DataSave::NodeAttributesSetValue(node, attr_name_param_time, mDoubleToString(param.time_ms).c_str(), true);
        return true;
    }
    return false;
}